package exp;

import utils.BasicJPanel;
import utils.VFlowLayout;

import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Path;

public class GrayTransformationJPanel extends BasicJPanel implements ActionListener, ChangeListener
{
    private final JLabel imageJLabel, grayJLabel, resultJLabel, resultHistogramJLabel, grayMapJLabel, grayHistogramJLabel, infoJLabel;


    private final JPanel   logJPanle;
    private final JSpinner logCJSpinner;

    private final JPanel    gammaJPanle;
    private final JSpinner  gammaCJSpinner;
    private final JSpinner  gammaYJSpinner;
    private final JCheckBox gammaAutoCJCheckBox;

    private final JPanel   line1JPanle;
    private final JSpinner line1Y0JSpinner;
    private final JSpinner line1Y1JSpinner;

    private final JPanel   line2JPanle;
    private final JSpinner line2X0JSpinner;
    private final JSpinner line2Y0JSpinner;
    private final JSpinner line2X1JSpinner;
    private final JSpinner line2Y1JSpinner;

    private final JPanel    equalJPanle;
    private final JCheckBox equalIgnoreCJCheckBox;

    private final JPanel standerJPanle;
    private final JLabel standerJLabel;
    private final JSpinner standerJSpinner;
    private final JCheckBox standerIgnoreCJCheckBox;

    private String type;

    public GrayTransformationJPanel()
    {
        super();
        setLayout(new BorderLayout());
                {
            JPanel jPanel=new JPanel();
            jPanel.setLayout(new GridLayout(2,3));
            add(jPanel);
            jPanel.add(imageJLabel=new JLabel());
            jPanel.add(grayJLabel=new JLabel());
            jPanel.add(resultJLabel=new JLabel());
            jPanel.add(grayMapJLabel=new JLabel());
            jPanel.add(grayHistogramJLabel=new JLabel());
            jPanel.add(resultHistogramJLabel=new JLabel());
        }
        add(infoJLabel=new JLabel(),BorderLayout.NORTH);


        JPanel optionJPanel=new JPanel();
        optionJPanel.setBorder(new TitledBorder("操作"));
        JScrollPane jScrollPane=new JScrollPane(optionJPanel);
        jScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        add(jScrollPane,BorderLayout.WEST);
        optionJPanel.setLayout(new VFlowLayout());

        JPanel jRadioButtonsJPanel=new JPanel();
        optionJPanel.add(jRadioButtonsJPanel);
        jRadioButtonsJPanel.setLayout(new GridLayout(3,3));
        String[] jRadioButtonsString={"不变换","对数变换","伽马变换","线性变换1","线性变换2","直方图均衡化","直方图标准化"};
        type=jRadioButtonsString[0];
        JRadioButton[] jRadioButtons=new JRadioButton[jRadioButtonsString.length];
        ButtonGroup jRadioButtonsButtonGroup=new ButtonGroup();
        for(int i=0;i<jRadioButtons.length;++i)
        {
            jRadioButtons[i]=new JRadioButton(jRadioButtonsString[i]);
            jRadioButtonsJPanel.add(jRadioButtons[i]);
            jRadioButtonsButtonGroup.add(jRadioButtons[i]);
            jRadioButtons[i].addActionListener(this);
        }
        jRadioButtons[0].setSelected(true);
        {
            logJPanle=new JPanel();
            optionJPanel.add(logJPanle);
            logJPanle.setLayout(new VFlowLayout());
            logJPanle.setVisible(false);
            logJPanle.setBorder(new TitledBorder("对数变换"));
            {
                JPanel jPanel=new JPanel();
                logJPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("c:"));
                logCJSpinner=new JSpinner(new SpinnerNumberModel(106,-1000,1000,1));
                jPanel.add(logCJSpinner);
                logCJSpinner.addChangeListener(this);
            }
            JButton JButton1=new JButton("恢复默认");
            JButton1.addActionListener(e->{
                logCJSpinner.setValue(20);
            });
            logJPanle.add(JButton1);
            JButton JButton2=new JButton("清零");
            JButton2.addActionListener(e->{
                logCJSpinner.setValue(1);
            });
            logJPanle.add(JButton2);
        }
        {
            gammaJPanle=new JPanel();
            optionJPanel.add(gammaJPanle);
            gammaJPanle.setLayout(new VFlowLayout());
            gammaJPanle.setVisible(false);
            gammaJPanle.setBorder(new TitledBorder("伽马变化，c:s=c*r^y"));
            {
                JPanel jPanel=new JPanel();
                gammaJPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("c:"));
                gammaCJSpinner=new JSpinner(new SpinnerNumberModel(18.4,-100,100,0.1));
                jPanel.add(gammaCJSpinner);
                gammaCJSpinner.addChangeListener(this);
            }
            {
                JPanel jPanel=new JPanel();
                gammaJPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("y:"));

                gammaYJSpinner=new JSpinner(new SpinnerNumberModel(0.46,0,25,0.01));
                jPanel.add(gammaYJSpinner);
                gammaYJSpinner.addChangeListener(this);
            }
            gammaAutoCJCheckBox=new JCheckBox("自动计算C",false);
            gammaJPanle.add(gammaAutoCJCheckBox);
            gammaAutoCJCheckBox.addActionListener(this);

            JButton JButton1=new JButton("恢复默认");
            JButton1.addActionListener(e->{
                gammaAutoCJCheckBox.setSelected(false);
                gammaCJSpinner.setEnabled(true);
                gammaCJSpinner.setValue(18.4);
                gammaYJSpinner.setValue(0.46);
            });
            gammaJPanle.add(JButton1);
            JButton JButton2=new JButton("清零");
            JButton2.addActionListener(e->{
                gammaAutoCJCheckBox.setSelected(false);
                gammaCJSpinner.setEnabled(true);
                gammaCJSpinner.setValue(1.0);
                gammaYJSpinner.setValue(1.0);
            });
            gammaJPanle.add(JButton2);
        }
        {
            line1JPanle=new JPanel();
            optionJPanel.add(line1JPanle);
            line1JPanle.setLayout(new VFlowLayout());
            line1JPanle.setVisible(false);
            line1JPanle.setBorder(new TitledBorder("线性变换1"));
            {
                JPanel jPanel=new JPanel();
                line1JPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("y0:"));
                line1Y0JSpinner=new JSpinner(new SpinnerNumberModel(-60,-10000,10000,1));
                jPanel.add(line1Y0JSpinner);
                line1Y0JSpinner.addChangeListener(this);
            }
            {
                JPanel jPanel=new JPanel();
                line1JPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("y1:"));
                line1Y1JSpinner=new JSpinner(new SpinnerNumberModel(343,-10000,10000,1));
                jPanel.add(line1Y1JSpinner);
                line1Y1JSpinner.addChangeListener(this);
            }
            JButton JButton1=new JButton("恢复默认");
            JButton1.addActionListener(e->{
                line1Y0JSpinner.setValue(-60);
                line1Y1JSpinner.setValue(343);
            });
            line1JPanle.add(JButton1);
            JButton JButton2=new JButton("清零");
            JButton2.addActionListener(e->{
                line1Y0JSpinner.setValue(0);
                line1Y1JSpinner.setValue(255);
            });
            line1JPanle.add(JButton2);
        }
        {
            line2JPanle=new JPanel();
            optionJPanel.add(line2JPanle);
            line2JPanle.setLayout(new VFlowLayout());
            line2JPanle.setVisible(false);
            line2JPanle.setBorder(new TitledBorder("线性变换2"));
            {
                JPanel jPanel=new JPanel();
                line2JPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("x0:"));
                line2X0JSpinner=new JSpinner(new SpinnerNumberModel(10,0,255,1));
                jPanel.add(line2X0JSpinner);
                line2X0JSpinner.addChangeListener(this);
            }
            {
                JPanel jPanel=new JPanel();
                line2JPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("y0:"));
                line2Y0JSpinner=new JSpinner(new SpinnerNumberModel(40,0,255,1));
                jPanel.add(line2Y0JSpinner);
                line2Y0JSpinner.addChangeListener(this);
            }
            {
                JPanel jPanel=new JPanel();
                line2JPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("x1:"));
                line2X1JSpinner=new JSpinner(new SpinnerNumberModel(170,0,255,1));
                jPanel.add(line2X1JSpinner);
                line2X1JSpinner.addChangeListener(this);
            }
            {
                JPanel jPanel=new JPanel();
                line2JPanle.add(jPanel);
                jPanel.setLayout(new FlowLayout());
                jPanel.add(new JLabel("y1:"));
                line2Y1JSpinner=new JSpinner(new SpinnerNumberModel(140,0,255,1));
                jPanel.add(line2Y1JSpinner);
                line2Y1JSpinner.addChangeListener(this);
            }
            JButton JButton1=new JButton("恢复默认");
            JButton1.addActionListener(e->{
                line2X0JSpinner.setValue(10);
                line2Y0JSpinner.setValue(40);
                line2X1JSpinner.setValue(170);
                line2Y1JSpinner.setValue(140);
            });
            line2JPanle.add(JButton1);
            JButton JButton2=new JButton("清零");
            JButton2.addActionListener(e->{
                line2X0JSpinner.setValue(0);
                line2Y0JSpinner.setValue(0);
                line2X1JSpinner.setValue(255);
                line2Y1JSpinner.setValue(255);
            });
            line2JPanle.add(JButton2);
        }
        {
            equalJPanle=new JPanel();
            optionJPanel.add(equalJPanle);
            equalJPanle.setLayout(new VFlowLayout());
            equalJPanle.setVisible(false);
            equalJPanle.setBorder(new TitledBorder("直方图均衡化"));
            equalIgnoreCJCheckBox=new JCheckBox("不统计全白像素",false);
            equalJPanle.add(equalIgnoreCJCheckBox);
            equalIgnoreCJCheckBox.addActionListener(this);
        }
        {
            standerJPanle=new JPanel();
            optionJPanel.add(standerJPanle);
            standerJPanle.setLayout(new VFlowLayout());
            standerJPanle.setVisible(false);
            standerJPanle.setBorder(new TitledBorder("直方图标准化"));
            standerIgnoreCJCheckBox=new JCheckBox("不统计全白像素",false);
            standerJPanle.add(standerIgnoreCJCheckBox);
            standerIgnoreCJCheckBox.addActionListener(this);
            standerJSpinner=new JSpinner(new SpinnerNumberModel(4,0.1,10,0.1));
            standerJPanle.add(standerJSpinner);
            standerJSpinner.addChangeListener(this);
            standerJLabel=new JLabel();
            standerJPanle.add(standerJLabel);
        }
    }

    private int limit(int a)
    {
        return Math.min(255,Math.max(a,0));
    }

    private void update()
    {
        final BufferedImage src=(BufferedImage)((ImageIcon)imageJLabel.getIcon()).getImage();
        int[] grayMap=new int[256];
        switch(type)
        {
            case "不变换":
            {
                for(int i=0;i<256;++i)
                    grayMap[i]=i;
                break;
            }
            case "对数变换":
            {
                for(int i=0;i<256;++i)
                    grayMap[i]=limit((int)(((Integer)logCJSpinner.getValue())*Math.log10(1+i)));
                break;
            }
            case "伽马变换":
            {
                if(gammaAutoCJCheckBox.isSelected())
                {
                    gammaCJSpinner.setValue(255/Math.pow(255,(Double)gammaYJSpinner.getValue()));
                }
                for(int i=0;i<256;++i)
                    grayMap[i]=limit((int)(((Double)gammaCJSpinner.getValue())*Math.pow(i,(Double)gammaYJSpinner.getValue())));
                break;
            }
            case "线性变换1":
            {
                int x0=0;
                int x1=255;
                int y0=(Integer)line1Y0JSpinner.getValue();
                int y1=(Integer)line1Y1JSpinner.getValue();
                double k=(double)(y1-y0)/(x1-x0);
                double b=y1-k*x1;
                for(int i=0;i<256;++i)
                    grayMap[i]=limit((int)(i*k+b));
                break;
            }
            case "线性变换2":
            {
                {
                    int x0=0;
                    int y0=0;
                    int x1=(Integer)line2X0JSpinner.getValue();
                    int y1=(Integer)line2Y0JSpinner.getValue();
                    double k=(double)(y1-y0)/(x1-x0);
                    double b=y1-k*x1;
                    for(int i=x0;i<x1;++i)
                        grayMap[i]=limit((int)(i*k+b));
                }
                {
                    int x0=(Integer)line2X0JSpinner.getValue();
                    int y0=(Integer)line2Y0JSpinner.getValue();
                    int x1=(Integer)line2X1JSpinner.getValue();
                    int y1=(Integer)line2Y1JSpinner.getValue();
                    double k=(double)(y1-y0)/(x1-x0);
                    double b=y1-k*x1;
                    for(int i=x0;i<=x1;++i)
                        grayMap[i]=limit((int)(i*k+b));
                }
                {
                    int x0=(Integer)line2X1JSpinner.getValue();
                    int y0=(Integer)line2Y1JSpinner.getValue();
                    int x1=255;
                    int y1=255;
                    double k=(double)(y1-y0)/(x1-x0);
                    double b=y1-k*x1;
                    for(int i=x0+1;i<=x1;++i)
                        grayMap[i]=limit((int)(i*k+b));
                }
                break;
            }
            case "直方图均衡化":
            {
                final double[] cnt=new double[256];
                for(int i=0;i<256;++i)
                    cnt[i]=0;
                for(int i=0;i<src.getHeight();++i)
                    for(int j=0;j<src.getWidth();++j)
                        cnt[GrayJPanel.RGBToGray(src.getRGB(j,i))]+=1;
                for(int i=1;i<256;++i)
                    cnt[i]+=cnt[i-1];
                final int n=equalIgnoreCJCheckBox.isSelected()?255:256;
                for(int i=0;i<n;++i)
                    cnt[i]/=cnt[n-1];
                for(int i=0;i<n;++i)
                    grayMap[i]=(int)(cnt[i]*(n-1));
                grayMap[255]=255;
                break;
            }
            case "直方图标准化":
            {
                final double[] origin=new double[256], target=new double[256];
                for(int i=0;i<256;++i)
                    origin[i]=Math.abs(Math.sin(i/256.0*Math.PI*(Double)standerJSpinner.getValue()));
                target[0]=origin[0];
                for(int i=1;i<256;++i)
                    target[i]=target[i-1]+origin[i];
                for(int i=0;i<256;++i)
                    target[i]/=target[255];
                final double[] cnt=new double[256];
                for(int i=0;i<256;++i)
                    cnt[i]=0;
                for(int i=0;i<src.getHeight();++i)
                    for(int j=0;j<src.getWidth();++j)
                        cnt[GrayJPanel.RGBToGray(src.getRGB(j,i))]+=1;
                for(int i=1;i<256;++i)
                    cnt[i]+=cnt[i-1];
                final int n=standerIgnoreCJCheckBox.isSelected()?255:256;

                for(int i=0;i<n;++i)
                    cnt[i]/=cnt[n-1];
                for(int i=0, j=0;i<n;++i)
                {
                    while(j<255&&Math.abs(cnt[i]-target[j])>=Math.abs(cnt[i]-target[j+1]))
                        ++j;
                    grayMap[i]=j;
                }
                grayMap[255]=255;

                final BufferedImage img=new BufferedImage(256,256,BufferedImage.TYPE_INT_RGB);
                for(int i=0;i<256;++i)
                    for(int j=0;j<=origin[i]*255;++j)
                        img.setRGB(i,(img.getHeight()-j-1),0XFF00FF00);
                standerJLabel.setIcon(new ImageIcon(img));
                break;
            }
        }
        int resultCntMaxI=0, grayCntMaxI=0;
        final int[] resultCnt=new int[256], grayCnt=new int[256];
        for(int i=0;i<256;++i)
            resultCnt[i]=grayCnt[i]=0;


        final BufferedImage gry=new BufferedImage(src.getWidth(),src.getHeight(),BufferedImage.TYPE_INT_RGB);
        final BufferedImage tra=new BufferedImage(src.getWidth(),src.getHeight(),BufferedImage.TYPE_INT_RGB);
        for(int i=0;i<src.getHeight();++i)
            for(int j=0;j<src.getWidth();++j)
            {
                final int t=GrayJPanel.RGBToGray(src.getRGB(j,i));
                final int ct=grayMap[t];
                gry.setRGB(j,i,GrayJPanel.grayToRGB(t));
                tra.setRGB(j,i,GrayJPanel.grayToRGB(ct));
                ++resultCnt[ct];
                if(resultCnt[ct]>resultCnt[resultCntMaxI])
                    resultCntMaxI=ct;
                ++grayCnt[t];
                if(grayCnt[t]>grayCnt[grayCntMaxI])
                    grayCntMaxI=t;
            }
        infoJLabel.setText(String.format("处理前：最大频率@%d=%d %.2f%% | 处理后：最大频率@%d=%d %.2f%%",grayCntMaxI,grayCnt[grayCntMaxI],100.0*grayCnt[grayCntMaxI]/(src.getWidth()*src.getHeight()),resultCntMaxI,resultCnt[resultCntMaxI],100.0*resultCnt[resultCntMaxI]/(src.getWidth()*src.getHeight())));
        final BufferedImage resultHistogram=GrayHistogramJPanel.getHistogramImage(resultCntMaxI,resultCnt);
        final BufferedImage grayHistogram=GrayHistogramJPanel.getHistogramImage(grayCntMaxI,grayCnt);

        final BufferedImage grayMapImg=new BufferedImage(256,256,BufferedImage.TYPE_INT_RGB);
        for(int i=0;i<256;++i)
            for(int j=0;j<=grayMap[i];++j)
                grayMapImg.setRGB(i,(grayMapImg.getHeight()-j-1),0XFF00FF00);

        grayJLabel.setIcon(new ImageIcon(gry));
        resultJLabel.setIcon(new ImageIcon(tra));
        resultHistogramJLabel.setIcon(new ImageIcon(resultHistogram));
        grayHistogramJLabel.setIcon(new ImageIcon(grayHistogram));
        grayMapJLabel.setIcon(new ImageIcon(grayMapImg));
    }


    public void update(Path path) throws IOException, BmpStructureErrorException
    {
        final Bmp bmp=new Bmp(path);
        imageJLabel.setIcon(new ImageIcon(bmp.getImage()));
        update();
    }

    public String getButtonName()
    {
        return "<html>必做5:直方图均衡化<br>第5章选做1：对数变换<br>第5章选做2：伽马变换<br>第5章选做3：线性变换<br>第5章选做4：直方图标准化</html>";
    }

    public void actionPerformed(ActionEvent e)
    {
        if(e.getSource() instanceof JRadioButton)
        {
            type=((JRadioButton)e.getSource()).getText();
            switch(((JRadioButton)e.getSource()).getText())
            {
                case "对数变换":
                {
                    logJPanle.setVisible(true);
                    gammaJPanle.setVisible(false);
                    line1JPanle.setVisible(false);
                    line2JPanle.setVisible(false);
                    equalJPanle.setVisible(false);
                    standerJPanle.setVisible(false);
                    break;
                }
                case "伽马变换":
                {
                    logJPanle.setVisible(false);
                    gammaJPanle.setVisible(true);
                    line1JPanle.setVisible(false);
                    line2JPanle.setVisible(false);
                    equalJPanle.setVisible(false);
                    standerJPanle.setVisible(false);
                    break;
                }
                case "线性变换1":
                {
                    logJPanle.setVisible(false);
                    gammaJPanle.setVisible(false);
                    line1JPanle.setVisible(true);
                    line2JPanle.setVisible(false);
                    equalJPanle.setVisible(false);
                    standerJPanle.setVisible(false);
                    break;
                }
                case "线性变换2":
                {
                    logJPanle.setVisible(false);
                    gammaJPanle.setVisible(false);
                    line1JPanle.setVisible(false);
                    line2JPanle.setVisible(true);
                    equalJPanle.setVisible(false);
                    standerJPanle.setVisible(false);
                    break;
                }
                case "直方图均衡化":
                {
                    logJPanle.setVisible(false);
                    gammaJPanle.setVisible(false);
                    line1JPanle.setVisible(false);
                    line2JPanle.setVisible(false);
                    equalJPanle.setVisible(true);
                    standerJPanle.setVisible(false);
                    break;
                }
                case "直方图标准化":
                {
                    logJPanle.setVisible(false);
                    gammaJPanle.setVisible(false);
                    line1JPanle.setVisible(false);
                    line2JPanle.setVisible(false);
                    equalJPanle.setVisible(false);
                    standerJPanle.setVisible(true);
                    break;
                }
                default:
                {
                    logJPanle.setVisible(false);
                    gammaJPanle.setVisible(false);
                    line1JPanle.setVisible(false);
                    line2JPanle.setVisible(false);
                    equalJPanle.setVisible(false);
                    standerJPanle.setVisible(false);
                    break;
                }
            }
        }
        gammaCJSpinner.setEnabled(!gammaAutoCJCheckBox.isSelected());
        update();
    }

    @Override
    public void stateChanged(ChangeEvent e)
    {
        update();
    }
}
